// Officer.cs
//
// Copyright (c) 2009 Mike Strobel
//
// This source code is subject to the terms of the Microsoft Reciprocal License (Ms-RL).
// For details, see <http://www.opensource.org/licenses/ms-rl.html>.
//
// All other rights reserved.

using System;
using System.ComponentModel;
using System.Runtime.Serialization;

using Supremacy.Annotations;
using Supremacy.Collections;
using Supremacy.Entities;
using Supremacy.Game;
using Supremacy.IO.Serialization;
using Supremacy.Types;

using System.Linq;

using Supremacy.Utility;

namespace Supremacy.Personnel
{
    [Serializable]
    public class Agent : GameObject, IGameTurnListener
    {
        public const int NaturalSkillsPerAgent = 2;
            
        private short _ownerId = (short)Civilization.InvalidID;
        private string _profileName;
        private AgentCareer _career;
        private AgentSkillRatings _skillRatings;
        private AgentAssignment _assignment;

        [NonSerialized]
        private Lazy<AgentProfile> _profile;

        public Agent([NotNull] AgentProfile profile, [NotNull] Civilization owner)
        {
            if (profile == null)
                throw new ArgumentNullException("profile");
            if (owner == null)
                throw new ArgumentNullException("owner");

            _profile = new Lazy<AgentProfile>(LazyLoadProfile);

            Initialize(profile, owner);
        }

        public Agent(GameObjectID objectId, AgentProfile profile, Civilization owner)
            : base(objectId)
        {
            if (profile == null)
                throw new ArgumentNullException("profile");
            if (owner == null)
                throw new ArgumentNullException("owner");

            _profile = new Lazy<AgentProfile>(LazyLoadProfile);

            Initialize(profile, owner);
        }

        private Agent()
        {
            // For cloning purposes only...
            _profile = new Lazy<AgentProfile>(LazyLoadProfile);
        }
        
        private AgentProfile LazyLoadProfile()
        {
            return GameContext.Current.AgentDatabase[this.Owner][_profileName];
        }

        private void Initialize([NotNull] AgentProfile profile, [NotNull] Civilization owner)
        {
            _profileName = profile.Name;
            _ownerId = (short)owner.CivID;
            _skillRatings = new AgentSkillRatings();

            foreach (var agentSkill in EnumHelper.GetValues<AgentSkill>())
            {
                var startingSkill = profile.NaturalSkills.Contains(agentSkill)
                                        ? RandomProvider.Shared.Next(10, 15)
                                        : RandomProvider.Shared.Next(3, 8);

                _skillRatings[agentSkill].Reset(startingSkill);
            }

            _skillRatings.Keys.Select(o => _skillRatings[o]).ForEach(
                o => o.PropertyChanged += OnSkillRatingsPropertyChanged);
        }

        public GameObjectID OwnerID
        {
            get { return _ownerId; }
            set
            {
                if (value < 0)
                    value = Civilization.InvalidID;

                _ownerId = (short)value;

                OnPropertyChanged("OwnerID");
            }
        }

        [CanBeNull]
        public Civilization Owner
        {
            get { return GameContext.Current.Civilizations[_ownerId]; }
        }

        public AgentCareer Career
        {
            get { return _career; }
            internal set { _career = value; }
        }

        [NotNull]
        public AgentProfile Profile
        {
            get { return _profile.Value; }
        }

        [NotNull]
        public AgentSkillRatings SkillRatings
        {
            get
            {
                if (_skillRatings == null)
                    _skillRatings = new AgentSkillRatings();

                return _skillRatings;
            }
        }

        [CanBeNull]
        public AgentAssignment Assignment
        {
            get { return _assignment; }
            internal set { _assignment = value; }
        }

        private void OnSkillRatingsPropertyChanged(object sender, PropertyChangedEventArgs args)
        {
            this.OnPropertyChanged("SkillRatings");
        }

        #region IOwnedDataSerializableAndRecreatable Members
        public override void DeserializeOwnedData([NotNull] SerializationReader reader, [CanBeNull] object context)
        {
            base.DeserializeOwnedData(reader, context);

            _ownerId = reader.ReadInt16();
            _profileName = reader.ReadString();
            _skillRatings = reader.Read<AgentSkillRatings>();

            if (_profile == null)
                _profile = new Lazy<AgentProfile>(LazyLoadProfile);
        }

        public override void SerializeOwnedData([NotNull] SerializationWriter writer, [CanBeNull] object context)
        {
            base.SerializeOwnedData(writer, context);
            
            writer.Write(_ownerId);
            writer.Write(_profileName);
            writer.WriteObject(_skillRatings);
        }
        #endregion

        [OnDeserialized]
        private void OnDeserialized(StreamingContext context)
        {
            if (_profile == null)
                _profile = new Lazy<AgentProfile>(LazyLoadProfile);
        }

        protected override Cloneable CreateInstance()
        {
            return new Agent();
        }

        public override void CloneFrom(Cloneable source)
        {
            var typedSource = (Agent)source;
            
            base.CloneFrom(source);

            _ownerId = typedSource._ownerId;
            _profileName = typedSource._profileName;

            if (_profile == null)
                _profile = new Lazy<AgentProfile>(LazyLoadProfile);

            if (_skillRatings == null)
                _skillRatings = new AgentSkillRatings();

            _skillRatings.Keys.ForEach(o => _skillRatings[o].CloneFrom(typedSource._skillRatings[o]));
        }

        #region Implementation of IGameTurnListener

        void IGameTurnListener.OnTurnStarted(GameContext game)
        {
            var assignment = this.Assignment as IGameTurnListener;
            if (assignment != null)
                assignment.OnTurnStarted(game);
        }

        void IGameTurnListener.OnTurnPhaseStarted(GameContext game, TurnPhase phase)
        {
            var assignment = this.Assignment as IGameTurnListener;
            if (assignment != null)
                assignment.OnTurnPhaseStarted(game, phase);
        }

        void IGameTurnListener.OnTurnPhaseFinished(GameContext game, TurnPhase phase)
        {
            var assignment = this.Assignment as IGameTurnListener;
            if (assignment != null)
                assignment.OnTurnPhaseFinished(game, phase);
        }

        void IGameTurnListener.OnTurnFinished(GameContext game)
        {
            var assignment = this.Assignment as IGameTurnListener;
            if (assignment != null)
                assignment.OnTurnFinished(game);
        }

        #endregion
    }
}